/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package cz.vse.adventura.po1715.xshet00_shevtsova.gui;

import cz.pecinovsky.english.pojj.adv_framework.game_gui.IGameG;
import cz.pecinovsky.english.pojj.adv_framework.game_gui.IListener;
import cz.pecinovsky.english.pojj.adv_framework.game_gui.IUIG;
import cz.pecinovsky.english.pojj.adv_framework.game_txt.IGame;
import cz.pecinovsky.english.pojj.adv_framework.test_util._Test_115;
import cz.vse.adventura.po1715.xshet00_shevtsova.Obecne;
import cz.vse.adventura.po1715.xshet00_shevtsova.hra.Hra_destnik;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenuBar;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;


/*******************************************************************************
 * Instance třídy {@code GUI_6_XPECR999_Pecinovsky} přestavují grafické
 * uživatelské rozhraní hry implementující rozhraní {@link IGameG}.
 * <br><br>
 * Oproti minulé verzi:
 * <ul>
 *   <li>Upravuje uživatelské rozhraní tak, aby bylo možno definovat předměty
 *     a sousedy jako tlačítka, po jejichž stisku se provede příslušný příkaz.
 *     </li>
 *   <li>Do panelu se vstupním polem přidává tlačítko <b>Konec</b>
 *     jehož stiskem je možné ukončit aktuální hru.</li>
 * </ul>
 *
 * @author    Rudolf PECINOVSKÝ
 * @version   0.00.000
 */
public class GUI_7_Shevtsova implements IUIG, IMojeGUI
{
//== KONSTANTNÍ ATRIBUTY TŘÍDY =================================================

    /** Pozice aplikačního okna připravená práci na počítači s více monitory. */
    private static final Point POZICE_0 = Obecne.POZICE_0;

    /** Implicitní hra použitá v případě, že nebude zadána žádná jiná. */
    private static final IGameG IMPLICITNÍ_HRA = Hra_destnik.getHra();



//== PROMĚNNÉ ATRIBUTY TŘÍDY ===================================================
//== STATICKÝ INICIALIZAČNÍ BLOK - STATICKÝ KONSTRUKTOR ========================
//== KONSTANTNÍ ATRIBUTY INSTANCÍ ==============================================

    /** Aplikační okno hry. */
    private final JFrame aplikačníOkno = new JFrame();

    /** Vstupní textové pole pro zadávání příkazů. */
    private final JTextField vstupníPole = new JTextField(40);

    /** Čeká na zadání příkazu, který pak předá obsluhované hře a
     *  zadaný příkaz spolu s obdrženou odpovědí zapíše do výstupního pole. */
    private final PosluchačVstupu posluchačVstupu = new PosluchačVstupu();

    /** Textová oblast, v níž se vypisují odpovědi hry na zadané příkazy. */
    private final JEditorPane výstupníPanel = new JEditorPane("text/html", "");

    /** Objekt určený pro postupné skládání jednotlivých odpovědí hry
     *  do jednoho souvislého textového řetězce. */
    private final StringBuilder skládačVýstupu = new StringBuilder();

    /** Panel zobrazující obsah batohu (předměty, které s v něm nacházejí. */
    private final PanelBatohuBtn panelBatohu = new PanelBatohuBtn(this);

    /** Panel zobrazující předměty v aktuální místnosti a její sousedy.*/
    private final PanelMístnostíBtn panelMístností =new PanelMístnostíBtn(this);

    /** Aplikační okno s plánkem hry. */
    private PlánekHry plánekHry = new PlánekHry(this);

    /** Objekt řídící zaznamenávání zadaných příkazu. */
    private IListener<String> žurnálPříkazů;



//== PROMĚNNÉ ATRIBUTY INSTANCÍ ================================================

    /** Hra, kterou dané gui právě ovládá. */
    private IGameG ovládanáHra;

    /** Příznak toho, zda se bude v průběhu hry zobrazovat její plánek. */
    private boolean zobrazovatPlánekHry = false;



//== PŘÍSTUPOVÉ METODY VLASTNOSTÍ TŘÍDY ========================================
//== OSTATNÍ NESOUKROMÉ METODY TŘÍDY ===========================================

//##############################################################################
//== KONSTRUKTORY A TOVÁRNÍ METODY =============================================

    /***************************************************************************
     * Vytvoří a zobrazí prázdné aplikační okno, jež čeká na zadání hry,
     * kterou bude obsluhovat.
     */
    public GUI_7_Shevtsova()
    {
        žurnálPříkazů = připravNabídku();
        připravPlánekHNry();

        připravPanelVýstupu();
        připravPanelVstupu();
        připravPanelBatohu();
        připravPanelMístností();

        //Zviditelnění aplikačního okna a úprava jeho velikosti a umístění
        aplikačníOkno.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        aplikačníOkno.pack();
        aplikačníOkno.setVisible(true);
        aplikačníOkno.setLocation(POZICE_0);
    }



//== ABSTRAKTNÍ METODY =========================================================
//== PŘÍSTUPOVÉ METODY VLASTNOSTÍ INSTANCÍ =====================================

    /***************************************************************************
     * Vrátí jméno autora/autorky programu ve tvaru "PŘÍJMENÍ Křestní",
     * tj. příjmení jako první a velkými písmeny, pak křestní jméno.
     *
     * @return Jméno autora/autorky programu ve tvaru PŘÍJMENÍ Křestní
     */
    @Override
    public String getAuthor()
    {
        return Obecne.AUTOR;
    }


    /***************************************************************************
     * Vrátí xname autora/autorky programu zapsané valkými písmeny.
     *
     * @return XNAME autora/autorky programu zapsané valkými písmeny
     */
    @Override
    public String getXNAME()
    {
        return Obecne.XNAME;
    }


    /***************************************************************************
     * Vrátí odkaz na aktuálně ovládanou hru.
     *
     * @return Odkaz na aktuálně ovládanou hru
     */
    @Override
    public IGameG getHra()
    {
        return ovládanáHra;
    }


    /***************************************************************************
     * Vrátí aktuální rozměr okna hry.
     *
     * @return Aktuální rozměr okna hry
     */
    @Override
    public Rectangle getOblast()
    {
        return aplikačníOkno.getBounds();
    }


    /***************************************************************************
     * Vrátí komponentu, která může sloužit jako rodič
     * otevíraných dialogových oken.
     *
     * @return Rodičovská komponenta
     */
    @Override
    public Component getRodiče()
    {
        return aplikačníOkno;
    }


    /***************************************************************************
     * Vrátí aktuální stav zobrazování plánku hry.
     *
     * @return Aktuální stav zobrazování plánku hry
     */
    @Override
    public boolean getZobrazovatPlánek()
    {
        return zobrazovatPlánekHry;
    }


    /***************************************************************************
     * Nastaví, zda se bude během hry zobrazovat plánek hry.
     *
     * @param zobrazovat {@code true} = bude se zobrazovat plánek hry
     */
    @Override
    public void setZobrazovatPlánek(boolean zobrazovat)
    {
        zobrazovatPlánekHry = zobrazovat;
        plánekHry.setVisible(zobrazovat);
    }



//== OSTATNÍ NESOUKROMÉ METODY INSTANCÍ ========================================

    /***************************************************************************
     * Spustí komunikaci touto instancí a zadanou hrou, která musí být
     * ve skutečnosti instancí rozhraní {@link IGameG}
     * a která nevíc nesmí být ještě rozběhnutá - rozbíhá ji až GUI.
     *
     * @param hra Hra, kterou ma dané GUI spustit
     * @throws IllegalArgumentException Pokud zadaná hra neimplementuje
     *         rozhraní {@link IGameG} nebo pokud je již rozběhnutá
     */
    @Override
    public void startGame( final IGame hra )
    {
        if (! (hra instanceof IGameG)) {
            throw new IllegalArgumentException( "\nSpouštěná hra" +
                "neimplementuje rozhraní adventura_115_10J.rámec.IGameG");
        }
        if (! hra.isReady()) {
            throw new IllegalArgumentException(
                "\nPřed dalším spuštěním je třeba hru nejprve ukončit");
        }
        this.ovládanáHra = (IGameG)hra;       //Zapamatuje si ovládanou hru
        aplikačníOkno.setTitle(                 //Podepíše aplikační okno
            "Autor GUI: " + getAuthor() + ",   Autor hry: " + hra.getAuthor());

        //Inicializuje podřízené panely
        inicializujPanelyVstupuVýstupu();
        panelBatohu   .inicializuj(ovládanáHra);
        panelMístností.inicializuj(ovládanáHra);
        plánekHry     .inicializuj(ovládanáHra);

        //Hru odstartujeme zadáním prázdného příkazu
        žurnálPříkazů.notice(null);    //Prázdný odkaz = nová hra
        executeCommand("");

        //Od této chvíle přebírá řízení hry posluchač vstupního pole
    }


    /***************************************************************************
     * Spustí komunikaci mezi implicitní hrou (ta nesmí v danou chvíli běžet)
     * a danou instancí GUI mající na starosti komunikaci s uživatelem.
     */
    @Override
    public void startGame()
    {
        ovládanáHra = IMPLICITNÍ_HRA;
        startGame(ovládanáHra);
    }



//+++ Přidáno pro rozšířené zadání v předmětu 4IT115 +++++++++++++++++++++++++++

    /***************************************************************************
     * Zpracuje příkaz předaný v parametru jako by byl zadán standardním
     * postupem z klávesnice. Metoda umožňuje testovat reakce daného
     * uživatelského rozhraní na příkazy zadávané z klávesnice.
     *
     * @param příkaz Zadávaný příkaz
     */
    @Override
    public void executeCommand(String příkaz)
    {
        vstupníPole.setText(příkaz);

        //Nemusím zadávat parametr volané metody,
        //protože ta si potřebné informce zjistí od pole txfVstup
        posluchačVstupu.actionPerformed(null);
    }



//== SOUKROMÉ A POMOCNÉ METODY TŘÍDY ===========================================
//== SOUKROMÉ A POMOCNÉ METODY INSTANCÍ ========================================

    /***************************************************************************
     * Inicializuje vstupní a výstupní panel pro práci s novou hrou.
     * Hlavní úlohou této metody je incializovat {@link #skládačVýstupu}.
     */
    private void inicializujPanelyVstupuVýstupu()
    {
        výstupníPanel.setText("");
        skládačVýstupu.setLength(0);
        skládačVýstupu.append("<html>");
    }


    /***************************************************************************
     * Připraví systém nabídek a vrátí nabídku, která bude mít na starosti
     * žurnál zaznamenávaných příkazů.
     *
     * @return Nabídka Žurnál
     */
    private IListener<String> připravNabídku()
    {
        JMenuBar hlavniNabidka = new JMenuBar();
        aplikačníOkno.setJMenuBar(hlavniNabidka);

        NabídkaHra      nabídkaHra      = new NabídkaHra     (this);
        NabídkaŽurnál   nabídkaŽurnál   = new NabídkaŽurnál  (this);
        NabídkaNápověda nabídkaNápověda = new NabídkaNápověda(this);

        hlavniNabidka.add(nabídkaHra);
        hlavniNabidka.add(nabídkaŽurnál);
        hlavniNabidka.add(nabídkaNápověda);

        return nabídkaŽurnál;
    }


    /***************************************************************************
     * Připraví v západní zóně aplikačního okna panel,
     * který bude sloužit zobrazení předmětů v batohu.
     */
    private void připravPanelBatohu()
    {
        aplikačníOkno.add(panelBatohu, BorderLayout.WEST);
    }


    /***************************************************************************
     * Připraví ve východní zóně aplikačního okna panel,
     * který bude sloužit zobrazení předmětů v aktální místnosti
     * a sousedů této místnosti, kam lze v daný okamžik přejít.
     */
    private void připravPanelMístností()
    {
        aplikačníOkno.add(panelMístností, BorderLayout.EAST);
    }


    /***************************************************************************
     * Připraví v jižní zóně aplikačního okna panel,
     * který bude sloužit pro standardní zadávání příkazů.
     * Současně přihlásí u vstupního pole jeho posluchače.
     */
    private void připravPanelVstupu()
    {
        //Popisek označující význam vstupního pole
        JLabel  popisekVstupu = new JLabel ("Zadej prikaz:");
        JButton tlačítkoKonec = new JButton("KONEC");

        //Spodní (jižní) panel pro zadávání příkazů
        JPanel dolniPanel = new JPanel();

        dolniPanel.add(popisekVstupu);
        dolniPanel.add(vstupníPole);
        dolniPanel.add(tlačítkoKonec);

        aplikačníOkno.add(dolniPanel, BorderLayout.SOUTH);
        vstupníPole  .addActionListener(posluchačVstupu);
        tlačítkoKonec.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                if (ovládanáHra != null) {
                    String příkaz = ovládanáHra.getBasicCommands().cEND;
                    executeCommand(příkaz);
                }
            }
        });
    }


    /***************************************************************************
     * Připraví v centrální zóně aplikačního okna panel,
     * v němž se budou zobrazovat všechny zprávy o průběhu hry.
     */
    private void připravPanelVýstupu()
    {
        výstupníPanel.setEditable(false);
        JScrollPane posuvnik = new JScrollPane(výstupníPanel);
        posuvnik.setPreferredSize(new Dimension(400, 300));
        aplikačníOkno.add(posuvnik, BorderLayout.CENTER);
    }


    /***************************************************************************
     *
     */
    private void připravPlánekHNry()
    {
    }



//== INTERNÍ DATOVÉ TYPY =======================================================

    /***************************************************************************
     * Instance třídy "poslouchá" události ve vstupním poli,
     * do nějž se zadávají příkazy, předá zadaný příkaz obsluhované hře
     * a zadaný příkaz spolu s obdrženou odpovědí zapíše do výstupního pole.
     */
    private class PosluchačVstupu implements ActionListener
    {
        /***********************************************************************
         * Požadovaným způsobem zareaguje na zadání příkazu,
         * tj. předá příkaz hře, převede její odpověď do formát HTML
         * a přidá ji spolu se zadaným příkazem na konec výpisu akcí.
         * Zabezpečí přitom, že zadaný příkaz je vysazen tučně a
         * odpověď hry netučně.
         *
         * @param e Nastalá událost
         */
        @Override
        public void actionPerformed(ActionEvent e)
        {
            String příkaz = vstupníPole.getText();
            žurnálPříkazů.notice(příkaz);

            skládačVýstupu.append("<b>").append(příkaz).append("</b><br>");

            String odpověď = ovládanáHra.executeCommand(příkaz);

            odpověď = odpověď.replace("\n", "<br>");
            skládačVýstupu.append(odpověď).append("<br><br>");
            výstupníPanel.setText(skládačVýstupu + "</html>");

            vstupníPole.setText(""); //Vyprázdnění obsahu vstupního pole
            //Přesunutí kurzoru na konec série zpráv, kterým dosáhneme
            //přesunutí posuvníku a zobrazení tohoto konce
            výstupníPanel.setCaretPosition(výstupníPanel.getDocument().
                                           getLength());
            aplikačníOkno.pack();
            vstupníPole.requestFocus();
        }
    }



//== TESTY A METODA MAIN =======================================================

    /***************************************************************************
     * Testovací metoda.
     */
    public static void test()
    {
        GUI_7_Shevtsova
               gui    = new GUI_7_Shevtsova();
        IGameG hra    = IMPLICITNÍ_HRA;
        _Test_115 test = _Test_115.getInstance(hra, gui);
        test.playGameByTestScenario();

        System.exit(0);
    }
    /** @param args Parametry příkazového řádku - nepoužívané. */
    public static void main( String[] args )  {  test();  }
}
